﻿namespace LogManager.Gui.ViewModels
{
    using LogManager.Core.Infrastructure.Logging;
    using LogManager.Core.Models;
    using LogManager.Core.Services;
    using LogManager.Core.Services.Location;
    using LogManager.Core.Services.Report;
    using LogManager.Gui.Helpers;
    using LogManager.Gui.UI.Representations;
    using LogManager.Gui.UI.Steps;
    using LogManager.Gui.ViewModels.Processing;
    using LogManager.Gui.Views;
    using LogManager.Utils.Helpers;
    using LogManager.Utils.Unity;
    using Microsoft.Practices.Unity;
    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Threading;
    using System.Threading.Tasks;
    using System.Windows.Documents;
    using System.Windows.Input;
    using LogManager.Gui.Factories;

    public class ExecuteArchivingViewModel : ViewModelBase<IExecuteArchivingView>, IExecuteArchivingViewModel
    {
        public virtual HierarchicalItemProcessModel Hierarchy { get; protected set; }
        public virtual bool IsScanning { get; set; }
        public virtual bool IsCancelling { get; set; }

        public virtual string CommandText { get; set; }
        public virtual string CommandImage { get; set; }
        public virtual FlowDocument SelectedItemDetails { get; set; }
        public virtual SimpleStatsModel Statistics { get; protected set; }

        public CancellationTokenSource CancelToken { get; private set; }
        public IConfiguration Configuration { get; private set; }

        public readonly ILocationService _checkLocationService;
        public readonly IReportService _reportService;
        public readonly IProcessingModelFactory _processingModelFactory;
        public ExecuteArchivingViewModel(IExecuteArchivingView view,
            IConfiguration configuration,
            ILocationService checkLocationService,
            IReportService reportService,
            IProcessingModelFactory processingModelFactory)

            : base(view)
        {
            this.Configuration = configuration;
            this.Statistics = DI.Container.Resolve<SimpleStatsModel>();
            this._checkLocationService = checkLocationService;
            this._reportService = reportService;
            this._processingModelFactory = processingModelFactory;
            this.SetCommandsTo(WorkState.Ready);
        }

        private void InitView(IConfiguration configuration)
        {
            this.Hierarchy = _processingModelFactory.CreateHierarchicalItemProcessModel();
            this.Hierarchy.InitFromConfiguration(configuration, _processingModelFactory.CreateItemProcessModel);
            this.UpdateStatistics();
        }

        private void UpdateStatistics()
        {
            var locationModels = Hierarchy.RootItems.Flatten(p => p.Item is ILocation, p => p.Children);
            this.Statistics = GetStatsForModels(locationModels);
        }

        private SimpleStatsModel GetStatsForModels(IEnumerable<ItemProcessModel> models)
        {
            var simpleStats = DI.Container.Resolve<SimpleStatsModel>();
            simpleStats.Succeded = models.Count(p => p.ProcessingState == ProcessingState.Succeded);
            simpleStats.Failed = models.Count(p => p.ProcessingState == ProcessingState.Failed);
            simpleStats.Overall = models.Count();
            return simpleStats;
        }

        private void OnLocationArchivingProgress(LocationArchivingProgress progress)
        {
            switch (progress.LocationArchivingStep)
            {
                case LocationArchivingStep.CheckSource:
                    OnLocationTested(new LocationCheckProgress(progress.Location, progress.SourceResult, LocationCheckPath.Source, progress.OperationState));
                    break;
                case LocationArchivingStep.CheckDestination:
                    OnLocationTested(new LocationCheckProgress(progress.Location, progress.DestinationResult, LocationCheckPath.Destination, progress.OperationState));
                    break;
                case LocationArchivingStep.CheckFiles:
                    OnFilesCollected(new LocationFilesSearchProgress(progress.Location, progress.LogFilesSearchResult, progress.OperationState));
                    break;
                default:
                    break;
            }
        }

        private void OnLocationTested(LocationCheckProgress progress)
        {
            var locationModel = Hierarchy.Find(progress.Location);
            ItemProcessModel ItemModel = null;

            switch (progress.OperationState)
            {
                case OperationState.Starting:
                    ItemModel = DI.Container.Resolve<ItemProcessModel>();
                    ItemModel.Parent = locationModel;
                    ItemModel.ProcessingState = ProcessingState.Processing;
                    ItemModel.SecondaryImage = "/Images/networkFolder.png";

                    switch (progress.LocationPath)
                    {
                        case LocationCheckPath.Source:
                            ItemModel.Name = progress.Location.FullPathSource;
                            ItemModel.Item = LocationCheckPath.Source;
                            break;
                        case LocationCheckPath.Destination:
                            ItemModel.Name = progress.Location.FullPathDestination;
                            ItemModel.Item = LocationCheckPath.Destination;
                            break;
                        default:
                            throw new InvalidEnumArgumentException();
                    }
                    locationModel.Children.Add(ItemModel);
                    break;
                case OperationState.Ended:
                    ItemModel = locationModel.Children.Single(p => progress.LocationPath.Equals(p.Item));
                    ItemModel.Item = progress.ServiceStatusResult;

                    if (progress.ServiceStatusResult.Succeeded)
                        ItemModel.ProcessingState = ProcessingState.Succeded;
                    else
                    {
                        ItemModel.ProcessingState = ProcessingState.Failed;
                    }
                    break;
                default:
                    throw new InvalidEnumArgumentException();
            }

            this.View.BringIntoView(ItemModel);
        }

        private void OnItemFinished(object owner)
        {
            this.UpdateStatistics();
            var ownerModel = Hierarchy.Find(owner);
            if (ownerModel.Children.Any(p => p.ProcessingState == ProcessingState.Failed))
                ownerModel.ProcessingState = ProcessingState.Failed;
            else
                ownerModel.ProcessingState = ProcessingState.Succeded;
        }

        private void OnFilesCollected(LocationFilesSearchProgress progress)
        {
            var locationModel = Hierarchy.Find(progress.Location);

            switch (progress.OperationState)
            {
                case OperationState.Starting:

                    break;
                case OperationState.Ended:
                    var result = progress.LogFilesSearchResult;
                    if (result.Succeeded)
                    {
                        if (result.Files.Count == 0)
                        {
                            var ItemModel = DI.Container.Resolve<ItemProcessModel>();
                            ItemModel.Name = "No files found";
                            locationModel.Children.Add(ItemModel);
                        }

                        var locationFullPath = progress.Location.FullPathSource;
                        foreach (var file in result.Files)
                        {
                            var ItemModel = DI.Container.Resolve<ItemProcessModel>();
                            ItemModel.Name = file.LocationRelativePath;
                            ItemModel.SecondaryImage = "/Images/micro/file.png";
                            ItemModel.Item = file;
                            locationModel.Children.Add(ItemModel);
                        }
                    }
                    else
                    {
                        var ItemModel = DI.Container.Resolve<ItemProcessModel>();
                        ItemModel.Name = "Cound not load files";
                        ItemModel.ProcessingState = ProcessingState.Failed;
                        ItemModel.Item = progress.LogFilesSearchResult;
                        locationModel.Children.Add(ItemModel);
                    }
                    break;
                default:
                    throw new InvalidEnumArgumentException();
            }
        }

        private void OnLogFileArchivingProgress(LogFileArchivingProgress archivingProgress)
        {
            var logFile = archivingProgress.LogFile;
            var logFileModel = Hierarchy.Find(logFile);

            switch (logFile.LogFileStep)
            {
                case LogFileArchivingStep.InSource:
                    logFileModel.ProcessingState = ProcessingState.Processing;
                    break;
                case LogFileArchivingStep.Archived:
                    logFileModel.SecondaryImage = "/Images/utils/zip.png";
                    break;
                case LogFileArchivingStep.SourceCleared:
                    logFileModel.ProcessingState = ProcessingState.Succeded;
                    break;
                default:
                    throw new InvalidEnumArgumentException();
            }

            if (logFile.Error != null)
                logFileModel.ProcessingState = ProcessingState.Failed;
            this.View.BringIntoView(logFileModel);
        }

        public void OnTreeItemSelected(object selectedItem)
        {
            if (selectedItem == null)
                SelectedItemDetails = new FlowDocument();

            SelectedItemDetails = DetailsCreator.DetailsFor(((ItemProcessModel)selectedItem).Item);
        }

        private void ReportEnd()
        {
            var LogFileModels = Hierarchy.RootItems.Flatten(p => p.Item is LogFile, p => p.Children);
            var filesStats = GetStatsForModels(LogFileModels);

            if (this.Statistics.Overall == this.Statistics.Succeded)
                Log.Logger.AddSuccess("L'archivage s'est déroulé avec succès", "Fichiers: \n" + filesStats.ToString());
            else
                Log.Logger.AddError("Des erreures sont survenues durant l'archivage", "Fichiers: \n" + filesStats.ToString());
        }

        private async Task SendReport(LogArchivingResult archivingResult)
        {
            Log.Logger.AddInfo("Préparation de l'email", "");
            var emailResult = await this._reportService.SendReportAsync(archivingResult);
            if (emailResult.Succeeded)
                Log.Logger.AddSuccess("Email envoyé avec succès", "");
            else
                Log.Logger.AddError("Echec d'envoie du mail", emailResult.Message);
        }

        #region Commands

        public ICommand ToggleScanCommand { get { return new DelegateCommand(OnToggleScan); } }
        private void OnToggleScan()
        {
            if (this.IsScanning)
            {
                this.OnCancelScan();
            }
            else
            {
                this.OnLaunchScan();
            }
        }

        public ICommand LaunchScanCommand { get { return new DelegateCommand(OnLaunchScan); } }
        private async void OnLaunchScan()
        {
            this.IsScanning = true;
            this.SetCommandsTo(WorkState.Working);
            this.CancelToken = new CancellationTokenSource();
            var configurationCopy = this.Configuration.Clone();
            this.InitView(configurationCopy);

            Log.Logger.AddInfo("Scan démarré", "");
            var LocationArchivingProgress = new Progress<LocationArchivingProgress>(OnLocationArchivingProgress);

            var itemFinished = new Progress<object>(OnItemFinished);
            var logFileProgress = new Progress<LogFileArchivingProgress>(OnLogFileArchivingProgress);

            try
            {
                var archivingResult = await _checkLocationService.LaunchArchivingAsync(
                     configurationCopy,
                     this.CancelToken.Token,
                     LocationArchivingProgress,
                     itemFinished,
                     logFileProgress);

                if (configurationCopy.Settings.SmtpSettings.EnableNotifications)
                    await SendReport(archivingResult);

                this.ReportEnd();
            }
            catch (OperationCanceledException ex)
            {
                Log.Logger.AddWarning("L'archivage a été annulé", ex.Message);
            }
            finally
            {
                this.IsCancelling = false;
                this.IsScanning = false;
                this.SetCommandsTo(WorkState.Ready);
            }
        }

        public ICommand CancelScanCommand { get { return new DelegateCommand(OnCancelScan); } }
        private void OnCancelScan()
        {
            this.CancelToken.Cancel();
            this.IsCancelling = true;
            this.SetCommandsTo(WorkState.Cancelling);
        }
        #endregion

        #region UI
        private void SetCommandsTo(WorkState workState)
        {
            switch (workState)
            {
                case WorkState.Ready:
                    this.CommandText = "Lancer";
                    this.CommandImage = "/images/launch.png";
                    break;
                case WorkState.Working:
                    this.CommandText = "Arrêter";
                    this.CommandImage = "/images/stop.png";
                    break;
                case WorkState.Cancelling:
                    this.CommandText = "Arrêt en cours";
                    break;
                default:
                    break;
            }
        }
        #endregion
    }
}
